﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using System.IO;
using System.Globalization;
using System.Collections;
using CubeRotation;

namespace GestureSaver
{
    public enum GestureType
    { 
        UnknownGestureType = -1,
        RightHandPushUp = 0,
        LeftHandPushUp = 1,
        RightHandPullDown = 2,
        LeftHandPullDown = 3,
        RightHandSwipeLeft = 4,
        LeftHandSwipeLeft = 5,
        RightHandSwipeRight = 6,
        LeftHandSwipeRight = 7,
        RightHandCircleCW = 8,
        LeftHandCircleCW = 9,
        RightHandCircleCCW = 10,
        LeftHandCircleCCW = 11,
        RightHandWave = 12,
        LeftHandWave = 13,
        BothHandsZoomIn = 14,
        BothHandsZoomOut = 15,
        BothHandsPushUp = 16,
        BothHandsPullDown = 17
    }

    [Serializable]
    public class Gesture : IEnumerable
    {
        private DateTime startingTime;
        internal List<MSkeleton> data;
        public GestureType gestureType;
        public string inputFileName;

        public MSkeleton this[int index]
        {
            get
            {
                return data[index];
            }
        }

        public int SkeletonCount
        {
            get
            {
                return data.Count;
            }   
        }

        internal Gesture()
        {
            data = new List<MSkeleton>();
        }

        public Gesture(string fileName)
        {
            if (!File.Exists(fileName))
            {
                throw new Exception("The file does not exists!");
            }

            this.inputFileName = fileName;

            using (TextReader reader = File.OpenText(fileName))
            {
                string line = reader.ReadLine();

                while (line != null)
                {
                    if (line.StartsWith("//"))
                    {
                        line = reader.ReadLine();
                        continue;
                    }
                    if (line.StartsWith("@"))
                    {
                        this.gestureType = (GestureType)Enum.Parse(typeof(GestureType), line.Substring(1));
                        line = reader.ReadLine();
                        data = new List<MSkeleton>(Convert.ToInt32(line));
                        line = reader.ReadLine();
                        continue;
                    }
                    if (line.StartsWith("#"))
                    {
                        int index = Convert.ToInt32(line.Substring(1));
                        data.Add(new MSkeleton());
                        line = reader.ReadLine();
                        data[index].timeDifference = TimeSpan.Parse(line, CultureInfo.InvariantCulture);
                        line = reader.ReadLine();
                        while (line != null && line.StartsWith("&"))
                        {
                            string jointName = line.Substring(1);

                            JointType jointType = (JointType)Enum.Parse(typeof(JointType), jointName);

                            float X = Convert.ToSingle(reader.ReadLine(), CultureInfo.InvariantCulture);
                            float Y = Convert.ToSingle(reader.ReadLine(), CultureInfo.InvariantCulture);
                            float Z = Convert.ToSingle(reader.ReadLine(), CultureInfo.InvariantCulture);

                            data[index][jointType].Position.X = X;
                            data[index][jointType].Position.Y = Y;
                            data[index][jointType].Position.Z = Z;

                            line = reader.ReadLine();
                        }
                    }
                }
            }
        }
        

        public Gesture GetSubset(int startingFrame, int endingFrame)
        {
            Gesture rVal = new Gesture();

            rVal.gestureType = this.gestureType;
            rVal.inputFileName = this.inputFileName;
            
            for(int i=startingFrame; i<=endingFrame; i++)
            {
                MSkeleton cloned = DeepCloner.DeepClone<MSkeleton>(this.data[i]);
                cloned.timeDifference = this.data[i].timeDifference - this.data[startingFrame].timeDifference;
                rVal.data.Add(cloned);
            }

            return rVal;
        }

        internal void AddFrame(JointCollection jointCol, DateTime dt)
        {
            MSkeleton mSkeleton = new MSkeleton();

            if (data.Count == 0) // first frame
            {
                startingTime = DateTime.Now;                
            }

            mSkeleton.timeDifference = DateTime.Now - startingTime;

            foreach (Joint j in jointCol)
            {
                mSkeleton[j.JointType].Position.X = j.Position.X;
                mSkeleton[j.JointType].Position.Y = j.Position.Y;
                mSkeleton[j.JointType].Position.Z = j.Position.Z;
            }

            data.Add(mSkeleton);
        }

        public void SaveToFile(string fileName)
        {
            using (TextWriter writer = File.CreateText(fileName))
            {
                writer.WriteLine("//Istanbul Sehir University Machine Learning Labs Gesture Database by Istanbul Sehir University Machine");
                writer.WriteLine("//Learning Labs is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License.");
                writer.WriteLine("//Based on a work at www.sehir.edu.tr.");

                writer.WriteLine("@" + gestureType.ToString());
                writer.WriteLine(data.Count.ToString());

                for (int i = 0; i < data.Count; i++)
                {
                    writer.WriteLine("#" + i.ToString());
                    writer.WriteLine(data[i].timeDifference.ToString());

                    foreach (JointType type in Enum.GetValues(typeof(JointType)))
                    {
                        writer.WriteLine("&" + type.ToString());
                        writer.WriteLine(data[i][type].Position.X.ToString(CultureInfo.InvariantCulture));
                        writer.WriteLine(data[i][type].Position.Y.ToString(CultureInfo.InvariantCulture));
                        writer.WriteLine(data[i][type].Position.Z.ToString(CultureInfo.InvariantCulture));
                    }
                }

                writer.Close();
            }
        }

        public IEnumerator GetEnumerator()
        {
            return data.GetEnumerator();
        }

        static Random rnd = new Random();

        public static Gesture GetRandomGesture(string dbPath, GestureType gType = GestureType.UnknownGestureType)
        {
            if (Directory.Exists(dbPath))
            {                
                int randomNumber = 0;

                if (gType != GestureType.UnknownGestureType) // a specific gesture type is wanted.
                {
                    string directoryPath = dbPath + @"\" + gType.ToString();

                    if (Directory.Exists(directoryPath))
                    { 
                        string[] files = Directory.GetFiles(directoryPath);
                        randomNumber = rnd.Next(files.Length);
                        string selectedFile = files[randomNumber];
                        return new Gesture(selectedFile);
                    }
                }
                else
                {                    
                    string[] directories = Directory.GetDirectories(dbPath);
                    randomNumber = rnd.Next(directories.Length);
                    string selectedDirectory = directories[randomNumber];
                    string[] files = Directory.GetFiles(selectedDirectory);
                    randomNumber = rnd.Next(files.Length);
                    string selectedFile = files[randomNumber];
                    return new Gesture(selectedFile);
                }
            }

            throw new Exception("dbPath does not exists.");            
        }

        public void RotateX(double angle)
        {
            foreach (MSkeleton skeleton in this)
            {
                foreach (MJoint joint in skeleton)
                {
                    Point p = new Point(joint.Position.X, joint.Position.Y, joint.Position.Z);

                    p = MathTools.RotateX(p, angle);

                    joint.Position.X = (float)p.X;
                    joint.Position.Y = (float)p.Y;
                    joint.Position.Z = (float)p.Z;
                }
            }
        }

        public void RotateZ(double angle)
        {
            foreach (MSkeleton skeleton in this)
            {
                foreach (MJoint joint in skeleton)
                {
                    Point p = new Point(joint.Position.X, joint.Position.Y, joint.Position.Z);

                    p = MathTools.RotateZ(p, angle);

                    joint.Position.X = (float)p.X;
                    joint.Position.Y = (float)p.Y;
                    joint.Position.Z = (float)p.Z;
                }
            }
        }
    }
}
